package com.androsz.ramdroidx;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.RemoteViews;

public class UpdateWidgetService extends Service {
	private final class ScreenReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(final Context context, final Intent intent) {

			if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
				UpdateWidgetService.this.sendBroadcast(new Intent(RAMDroidWidget.ACTION_STOP_UPDATE_RAM));
			} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
				final SharedPreferences userPrefs = PreferenceManager
						.getDefaultSharedPreferences(UpdateWidgetService.this);
				final String updateMethod = userPrefs.getString(getString(R.string.prefs_key_update_method), "");
				if (updateMethod.equals("Continuous")) {
					UpdateWidgetService.this.sendBroadcast(new Intent(RAMDroidWidget.ACTION_START_UPDATE_RAM));
				} else {
					setReadyForTap();
				}
			}
		}
	}

	private final class UpdateWidgetRunner implements Runnable {
		private final int frequency;
		private boolean shouldRun;
		private final int blipDuration = 333;

		UpdateWidgetRunner(final int frequency) {
			final double power = 1 + (double) frequency / 10;
			this.frequency = (int) Math.pow(500, power) - blipDuration;

			shouldRun = true;
		}

		public void resume() {
			shouldRun = true;
		}

		@Override
		public void run() {
			if (!shouldRun) {
				return;
			}

			final ComponentName thisWidget = new ComponentName(UpdateWidgetService.this, RAMDroidWidget.class);
			final AppWidgetManager manager = AppWidgetManager.getInstance(UpdateWidgetService.this);
			final RemoteViews updateViews = new RemoteViews(UpdateWidgetService.this.getPackageName(), R.layout.widget_ramdroid);
			final ActivityManager.MemoryInfo mInfo = new ActivityManager.MemoryInfo();
			((ActivityManager) UpdateWidgetService.this.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(mInfo);
			final MemoryStorageUnit msu = MemoryStorageUnit.of(mInfo.availMem);
			final PendingIntent pendingIntent = PendingIntent.getBroadcast(
					UpdateWidgetService.this,
						0,
						new Intent(RAMDroidWidget.ACTION_STOP_UPDATE_RAM),
						0);

			final PendingIntent pendingIntentConfig = PendingIntent.getActivity(
					UpdateWidgetService.this,
								0,
								new Intent(UpdateWidgetService.this, SettingsActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP),
								0);

			updateViews.setViewVisibility(R.id.blip, View.VISIBLE);
			updateViews.setViewVisibility(R.id.ramunit, View.VISIBLE);
			final String freeRAM = msu.format(mInfo.availMem);
			final String units = msu.getUnits();
			updateViews.setTextViewText(R.id.ram, freeRAM);
			updateViews.setTextViewText(R.id.ramunit, units);
			updateViews.setOnClickPendingIntent(R.id.ramdroidwidget, pendingIntent);
			updateViews.setOnClickPendingIntent(R.id.buttonConfig, pendingIntentConfig);
			manager.updateAppWidget(thisWidget, updateViews);
			final SharedPreferences userPrefs = PreferenceManager
					.getDefaultSharedPreferences(UpdateWidgetService.this);
			final boolean raiseNotification = userPrefs.getBoolean(getString(R.string.prefs_key_notification), false);

			if (raiseNotification) {
				startForegroundCompat(NOTIFICATION_ID, createServiceNotification(updateViews));
				// raiseNotification(updateViews);
			}

			updateViews.setViewVisibility(R.id.blip, View.INVISIBLE);
			try {
				java.lang.Thread.sleep(blipDuration);
			} catch (final InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			manager.updateAppWidget(thisWidget, updateViews);
			if (raiseNotification) {
				startForegroundCompat(NOTIFICATION_ID, createServiceNotification(updateViews));
				// raiseNotification(updateViews);
			}
			serviceHandler.postDelayed(this, frequency);
		}

		public void stop() {
			shouldRun = false;
		}
	}

	private static final int NOTIFICATION_ID = 0x1337b;

	private static final Class[] mStartForegroundSignature = new Class[] {
			int.class, Notification.class };
	private static final Class[] mStopForegroundSignature = new Class[] {
			boolean.class };
	private Method mStartForeground;
	private Method mStopForeground;
	private final Object[] mStartForegroundArgs = new Object[2];
	private final Object[] mStopForegroundArgs = new Object[1];

	private Handler serviceHandler = null; // To handle the service
	private UpdateWidgetRunner updateWidgetRunner = null;

	private BroadcastReceiver screenOnOffReceiver;
	private NotificationManager notificationManager;

	private Notification createServiceNotification(final RemoteViews widget) {
		final int icon = R.drawable.icon;
		final CharSequence tickerText = getText(R.string.app_name);
		final long when = System.currentTimeMillis();

		final Notification notification = new Notification(icon, tickerText,
				when);

		notification.flags = Notification.FLAG_ONGOING_EVENT;
		notification.contentView = widget;

		getApplicationContext();

		// final CharSequence contentTitle = getText(R.string.app_name);
		// final CharSequence contentText = getText(R.string.notification_text);
		final Intent notificationIntent = new Intent(UpdateWidgetService.this, SettingsActivity.class);
		notificationIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP
				| Intent.FLAG_ACTIVITY_NEW_TASK);

		notification.contentIntent = PendingIntent.getActivity(this, 0,
				notificationIntent, 0);
		// notification.setLatestEventInfo(context, contentTitle, contentText,
		// contentIntent);

		return notification;
	}

	@Override
	public IBinder onBind(final Intent intent) {
		return null;
	}

	@Override
	public void onCreate() {
		final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
		filter.addAction(Intent.ACTION_SCREEN_OFF);
		screenOnOffReceiver = new ScreenReceiver();
		registerReceiver(screenOnOffReceiver, filter);

		notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		try {
			mStartForeground = getClass().getMethod("startForeground",
					mStartForegroundSignature);
			mStopForeground = getClass().getMethod("stopForeground",
					mStopForegroundSignature);
		} catch (final NoSuchMethodException e) {
			// Running on an older platform.
			mStartForeground = mStopForeground = null;
		}

		super.onCreate();
	}

	@Override
	public void onDestroy() {
		if (serviceHandler != null) {
			serviceHandler.removeCallbacks(updateWidgetRunner);
		}
		final SharedPreferences userPrefs = PreferenceManager
				.getDefaultSharedPreferences(this);
		final String updateMethod = userPrefs.getString(getString(R.string.prefs_key_update_method), "");
		if (!updateMethod.equals("Tap")) {
			setReadyForTap();
		}
		if (updateWidgetRunner != null) {
			updateWidgetRunner.stop();
		}

		unregisterReceiver(screenOnOffReceiver);

		if (notificationManager != null) {
			notificationManager.cancel(NOTIFICATION_ID);
		}

		super.onDestroy();
	}

	@Override
	public void onStart(final Intent intent, final int startId) {
		super.onStart(intent, startId);
		if (intent.getAction().equals(RAMDroidWidget.ACTION_READY_TAP)) {
			setReadyForTap();
			stopSelf(); // we don't need to stay in memory ;)
		} else if (intent.getAction().equals(RAMDroidWidget.ACTION_START_UPDATE_RAM)) {
			triggerUpdate();

		} else if (intent.getAction().equals(RAMDroidWidget.ACTION_STOP_UPDATE_RAM)) {
			stopSelf(); // we don't need to stay in memory ;)
		} else if (intent.getAction().equals(RAMDroidWidget.ACTION_UPDATE_RAM_ONCE)) {
			triggerUpdate();
			stopSelf();
		}
	}

	private void setReadyForTap() {
		final ComponentName thisWidget = new ComponentName(this, RAMDroidWidget.class);
		final AppWidgetManager manager = AppWidgetManager.getInstance(this);
		final RemoteViews updateViews = new RemoteViews(getPackageName(), R.layout.widget_ramdroid);
		final PendingIntent pendingIntent = PendingIntent.getBroadcast(
					this,
					0,
					new Intent(RAMDroidWidget.ACTION_START_UPDATE_RAM),
					0);

		final PendingIntent pendingIntentConfig = PendingIntent.getActivity(
							this,
							0,
							new Intent(UpdateWidgetService.this, SettingsActivity.class),
							0);

		updateViews.setViewVisibility(R.id.blip, View.INVISIBLE);
		updateViews.setViewVisibility(R.id.ramunit, View.INVISIBLE);
		updateViews.setTextViewText(R.id.ram, "Tap!");
		updateViews.setOnClickPendingIntent(R.id.ramdroidwidget, pendingIntent);
		updateViews.setOnClickPendingIntent(R.id.buttonConfig, pendingIntentConfig);

		manager.updateAppWidget(thisWidget, updateViews);
	}

	/**
	 * This is a wrapper around the new startForeground method, using the older
	 * APIs if it is not available.
	 */
	void startForegroundCompat(final int id, final Notification notification) {
		// If we have the new startForeground API, then use it.
		if (mStartForeground != null) {
			mStartForegroundArgs[0] = Integer.valueOf(id);
			mStartForegroundArgs[1] = notification;
			try {
				mStartForeground.invoke(this, mStartForegroundArgs);
			} catch (final InvocationTargetException e) {
				// Should not happen.
			} catch (final IllegalAccessException e) {
				// Should not happen.
			}
			return;
		}
		// Fall back on the old API.
		setForeground(true);
		notificationManager.notify(id, notification);
	}

	/**
	 * This is a wrapper around the new stopForeground method, using the older
	 * APIs if it is not available.
	 */
	void stopForegroundCompat(final int id) {
		// If we have the new stopForeground API, then use it.
		if (mStopForeground != null) {
			mStopForegroundArgs[0] = Boolean.TRUE;
			try {
				mStopForeground.invoke(this, mStopForegroundArgs);
			} catch (final InvocationTargetException e) {
				// Should not happen.
			} catch (final IllegalAccessException e) {
				// Should not happen.
			}
			return;
		}
		// Fall back on the old API. Note to cancel BEFORE changing the
		// foreground state, since we could be killed at that point.
		notificationManager.cancel(id);
		setForeground(false);
	}

	private void triggerUpdate() {
		serviceHandler = new Handler();
		if (updateWidgetRunner == null) {
			final SharedPreferences userPrefs = PreferenceManager
					.getDefaultSharedPreferences(this);
			final int updateFrequency = userPrefs.getInt(getString(R.string.prefs_key_update_frequency), 0);
			updateWidgetRunner = new UpdateWidgetRunner(updateFrequency);
		} else {
			updateWidgetRunner.resume();
		}
		serviceHandler.post(updateWidgetRunner);
	}
}
